﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using SoundMatcher.bean_classes;

namespace SoundMatcher
{
    class Program
    {
        

        static void Main(string[] args)
        {

            Program p=new Program();

            p.matchQueryWithSongs();
            
            //p.storeFrequencies();
       
     

        }

        public void matchQueryWithSongs()
        {
            string pathToSegment = "C:/Documents and Settings/Administrator/Desktop/SIHINEN_NIM.wav";

            byte[] bytesOfSegment = File.ReadAllBytes(pathToSegment);


            double[] segmentFrequency = getFrequencies(bytesOfSegment);
           

            SoundMatcher matcher = new SoundMatcher();

           List<int> matchingSongList= matcher.GetBestFit(segmentFrequency);


           foreach (int id in matchingSongList)
           {
               Console.WriteLine(id);
           }
           

        }

        public void storeFrequencies()
        {
            StreamReader sr = new StreamReader(@"OriginalSongs.xml");

            XmlTextReader reader = new XmlTextReader(sr);

            XmlDocument testSamples = new XmlDocument();

            testSamples.Load(reader);

            XmlNodeList samples = testSamples.SelectNodes("OriginalSongs/Song");


            List<string> pathList = new List<string>();


            foreach (XmlNode sample in samples)
            {
                string pathToOriginal = sample.InnerText;
                pathList.Add(pathToOriginal);
              
               
            }
            int i=0;
            foreach (string path in pathList)
            {
                

                 byte[] bytesOfOriginal = File.ReadAllBytes(path);

                 double[] originalFrequncies = getFrequencies(bytesOfOriginal);
                  int samplesInOriginal = bytesOfOriginal.Length / 16384;
                
                 int[] freDiference=new int[samplesInOriginal-1];

                 for (int j = 0; j<(samplesInOriginal-1);j++ )//loop to calculate fre difference from original frequency array
                 {
                     freDiference[j]=Convert.ToInt32(originalFrequncies[j+1]-originalFrequncies[j]);
                 }


                string freqString= String.Join(",", originalFrequncies.Select(n => Convert.ToInt32(n).ToString()).ToArray());
                string freqDiferenceString = String.Join(",", freDiference.Select(n => n.ToString()).ToArray());

                

                StringBuilder query = new StringBuilder("INSERT INTO song(title, frequencies,freq_difference)VALUES     ('song");
                query.Append(i+"','"+freqString+"','"+freqDiferenceString+"')");

               

                DB.Insert(query.ToString());
           
                i++;
            }

        }





        public void startComparison()
        {
            StreamReader sr = new StreamReader(@"TestSamples.xml");

            XmlTextReader reader = new XmlTextReader(sr);

            XmlDocument testSamples = new XmlDocument();

            testSamples.Load(reader);

            XmlNodeList samples = testSamples.SelectNodes("TestSamples/Sample");

            Program p = new Program();


            foreach (XmlNode sample in samples)
            {
                string pathToOriginal = sample.SelectSingleNode("Original").InnerText;
                string pathToSegment = sample.SelectSingleNode("Segment").InnerText;

                p.Compare(pathToOriginal, pathToSegment);

            }


        }



        public bool Compare(string pathToOriginal, string pathToSegment)
        {
                      

                 byte[] bytesOfOriginal = File.ReadAllBytes(pathToOriginal);

                 double[] originalFrequncies = getFrequencies(bytesOfOriginal);

                 byte[] bytesOfSegment = File.ReadAllBytes(pathToSegment);


                 double[] segmentFrequency = getFrequencies(bytesOfSegment);

                 int samplesInOriginal = bytesOfOriginal.Length / 16384;
                 int samplesInSegment = bytesOfSegment.Length / 16384;


                 int h = 0;
                 int x = 0;

                 List<double> accuracylist = new List<double>();


                 for (int i = 0; i <= samplesInOriginal - samplesInSegment; i++)
                 {



                     x = i;
                     int y = 0;
                     for (int j = 0; j < samplesInSegment; j++, x++)
                     {
                         double f1 = (segmentFrequency[j] - 20);        //set the proper range here
                         double f2 = originalFrequncies[x];
                         double f3 = (segmentFrequency[j] + 20);          //set the proper range here

                         if (f1 <= f2)
                         {
                             if (f2 <= f3)
                             {
                                 h++;

                             }
                         }


                     }

                     double noOfSimilarPoints = Convert.ToDouble(h);
                     double accuracy = noOfSimilarPoints / samplesInSegment;

                     accuracylist.Add(accuracy);



                     h = 0;

                 }




                 double maxAccuracy;
                    maxAccuracy=  accuracylist.Max();                        //taking the maximum accuracy out of the array


                    foreach (double accuracyValue in accuracylist)
                    {
                        if (accuracyValue >= .75)
                        {
                            Console.WriteLine(">>>>>>" + accuracyValue);
                        }
                    }
                    if (maxAccuracy >= .75)
                    {                            //set the accuracy of the matching here

                        Console.WriteLine("true  ");
                        Console.WriteLine("start index>>>>>" + (x - samplesInSegment) + "  accuracy>>>>>>>>>" + maxAccuracy);

                        return true;

                    }

///////////////////////**********code for changing the way of sampling of the original song and do the comparison***********///////////////////////////////////////////


                 samplesInOriginal -=1;

                 for (int k = 1; k < 16384; k += 1000)
                 {
                     originalFrequncies = getFrequencies(bytesOfOriginal, k);


                     List<double> accuracylist1 = new List<double>();

                     for (int i = 0; i <= samplesInOriginal - samplesInSegment; i++)
                     {

                         x = i;
                         int y = 0;
                         for (int j = 0; j < samplesInSegment; j++, x++)
                         {
                             double f1 = (segmentFrequency[j] - 20);        //set the proper range here
                             double f2 = originalFrequncies[x];
                             double f3 = (segmentFrequency[j] + 20);          //set the proper range here

                             if (f1 <= f2)
                             {
                                 if (f2 <= f3)
                                 {
                                     h++;

                                 }
                             }


                         }

                         double noOfSimilarPoints = Convert.ToDouble(h);
                         double accuracy = noOfSimilarPoints / samplesInSegment;

                         accuracylist1.Add(accuracy);



                         h = 0;

                     }
                     maxAccuracy = accuracylist1.Max();                        //taking the maximum accuracy out of the array


                     foreach (double accuracyValue in accuracylist1)
                     {
                         if (accuracyValue >= .75)
                         {
                             Console.WriteLine(">>>>>>" + accuracyValue);
                         }
                     }
                     if (maxAccuracy >= .75)
                     {                            //set the accuracy of the matching here

                         Console.WriteLine("true  ");
                         Console.WriteLine("start index>>>>>" + (x - samplesInSegment) + "  accuracy>>>>>>>>>" + maxAccuracy);

                         return true;

                     }


                 }
                 
///////////////////////////////////////////******************************************/////////////////////////////////////////////////
               



                 Console.WriteLine("false");
                 return false;

        }

       

        double[] getFrequencies(byte[] bytesOfFile)
        {
            byte[] ff = new byte[16384];
            int numberOfSamples = (int)(bytesOfFile.Length / 16384);
            double[] Frequencies = new double[numberOfSamples];
            int h = 0;
            for (int j = 0; j < numberOfSamples; j++)
            {
                for (int i = 0; i < ff.Length; i++)
                {
                    ff[i] = bytesOfFile[h + i];

                }
                Frequencies[j] = Process(ff);
                
                h += ff.Length;
            }

            return Frequencies;
        }

        double[] getFrequencies(byte[] bytesOfFile,int startIndex)
        {
            byte[] ff = new byte[16384];
            int numberOfSamples = (int)(bytesOfFile.Length / 16384);
            double[] Frequencies = new double[numberOfSamples-1];
            int h = startIndex;
            for (int j = 0; j < numberOfSamples-1; j++)     // since sampling is done satarting from different indexes between 1-16384,no of considerable samples are 1 less than original no of samples 
            {
                for (int i = 0; i < ff.Length; i++)
                {
                    ff[i] = bytesOfFile[h + i];

                }
                Frequencies[j] = Process(ff);

                h += ff.Length;
            }

            return Frequencies;
        }

         public  double Process(byte[] hjj)   //returns the frequency of a sample
        {
            byte[] wave;

            //byte[] bytes = File.ReadAllBytes(@"C:/Documents and Settings/Administrator/Desktop/Sine_wave_440.wav");

            //byte[] bytes = File.ReadAllBytes(path);


            byte[] byteInFile = new byte[16384];

            for (int i = 0; i < byteInFile.Length; i++)
            {
                byteInFile[i] = hjj[i];

            }

            wave = byteInFile;

           
           double[] _waveLeft = new double[wave.Length / 4];
           // _waveRight = new double[wave.Length / 4];

           
                int h = 0;
                for (int i = 0; i < wave.Length; i += 4)
                {
                    _waveLeft[h] = (double)BitConverter.ToInt16(wave, i);
                    //if (IsDetectingEvents == true)
                    //    if (_waveLeft[h] > AmplitudeThreshold || _waveLeft[h] < -AmplitudeThreshold)
                    //        IsEventActive = true;
                    //_waveRight[h] = (double)BitConverter.ToInt16(wave, i + 2);
                    //if (IsDetectingEvents == true)
                    //    if (_waveLeft[h] > AmplitudeThreshold || _waveLeft[h] < -AmplitudeThreshold)
                    //        IsEventActive = true;
                    h++;
                }
           

            // Generate frequency domain data in decibels
           double[] _fftLeft = FourierTransform.FFT(ref _waveLeft);

            double min = double.MaxValue;
            double minHz = 0;
            double max = double.MinValue;
            double maxHz = 0;
            int samples = 44100;
            double scaleHz = (double)(samples / 2) / (double)_fftLeft.Length;

            for (int x = 0; x < _fftLeft.Length; x++)
            {
                double amplitude = _fftLeft[x];
                if (min > amplitude)
                {
                    min = amplitude;
                    minHz = (double)x * scaleHz;
                   
                }
                if (max < amplitude)
                {
                    max = amplitude;
                    maxHz = (double)x * scaleHz;
                    
                }
            }
            
            return maxHz;
          
        }

    }

    public class FourierTransform
    {
        static private int n, nu;

        static private int BitReverse(int j)
        {
            int j2;
            int j1 = j;
            int k = 0;
            for (int i = 1; i <= nu; i++)
            {
                j2 = j1 / 2;
                k = 2 * k + j1 - 2 * j2;
                j1 = j2;
            }
            return k;
        }

        static public double[] FFT(ref double[] x)
        {
            // Assume n is a power of 2
            n = x.Length;
            nu = (int)(Math.Log(n) / Math.Log(2));
            int n2 = n / 2;
            int nu1 = nu - 1;
            double[] xre = new double[n];
            double[] xim = new double[n];
            double[] magnitude = new double[n2];
            double[] decibel = new double[n2];
            double tr, ti, p, arg, c, s;
            for (int i = 0; i < n; i++)
            {
                xre[i] = x[i];
                xim[i] = 0.0f;
            }
            int k = 0;
            for (int l = 1; l <= nu; l++)
            {
                while (k < n)
                {
                    for (int i = 1; i <= n2; i++)
                    {
                        p = BitReverse(k >> nu1);
                        arg = 2 * (double)Math.PI * p / n;
                        c = (double)Math.Cos(arg);
                        s = (double)Math.Sin(arg);
                        tr = xre[k + n2] * c + xim[k + n2] * s;
                        ti = xim[k + n2] * c - xre[k + n2] * s;
                        xre[k + n2] = xre[k] - tr;
                        xim[k + n2] = xim[k] - ti;
                        xre[k] += tr;
                        xim[k] += ti;
                        k++;
                    }
                    k += n2;
                }
                k = 0;
                nu1--;
                n2 = n2 / 2;
            }
            k = 0;
            int r;
            while (k < n)
            {
                r = BitReverse(k);
                if (r > k)
                {
                    tr = xre[k];
                    ti = xim[k];
                    xre[k] = xre[r];
                    xim[k] = xim[r];
                    xre[r] = tr;
                    xim[r] = ti;
                }
                k++;
            }
            for (int i = 0; i < n / 2; i++)
                //magnitude[i] = (float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i])));
                decibel[i] = 10.0 * Math.Log10((float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i]))));
            //return magnitude;
            return decibel;
        }
    }


    }


